home *** CD-ROM | disk | FTP | other *** search
/ Complete Linux / Complete Linux.iso / docs / apps / database / postgres / postgre4.z / postgre4 / src / rules / prs2 / prs2stack.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-08-27  |  8.6 KB  |  291 lines

  1. /*===================================================================
  2.  *
  3.  * FILE:
  4.  *   prs2stack.c
  5.  *
  6.  * IDENTIFICATION:
  7.  *   $Header: /private/postgres/src/rules/prs2/RCS/prs2stack.c,v 1.4 1991/11/18 22:21:22 mer Exp $
  8.  *
  9.  * DESCRIPTION:
  10.  * This file contains some routines used to implement a loop detection
  11.  * mechanism.
  12.  *
  13.  * The Problem:
  14.  * Sometimes a rule in order to calculate the value of an attribute
  15.  * (that has a LockTypeWrite on it) need to execute a plan.
  16.  * This plan might retrieve the same tuple that caused the
  17.  * activation of the rule in the first place, thus reactivating the rule
  18.  * ans causing a loop. 
  19.  * For example, assume the rule:
  20.  *    ON retrieve to EMP.salary where EMP.name = "Mike"
  21.  *    DO INSTEAD retrieve (EMP.salary) where EMP.name = "Fred"
  22.  * This rule states that if we try to retrieve Mike's salary
  23.  * we must substitute Fred's salary instead.
  24.  * If a user tries to retrieve Mike's salary the above rule will be
  25.  * activated, and it will execute the plan:
  26.  *    retrieve (EMP.salary) where EMP.name = "Fred"
  27.  * Lets assume that the planner had decided to use a sequential scan
  28.  * and that Mike's it encounter's Mike's tuple before it encounter's
  29.  * Fred's. In this case the executor will try to retrieve both
  30.  * name and salary of Mike's tuple at the same time (it first
  31.  * retrieves all the attributes needed and *then* checks fo rthe
  32.  * qualification, i.e. to see if name == "Fred".
  33.  * So, as Mike's salary is retrieved, then the rule manager will
  34.  * reactivate the rule above, which will try again to run the plan
  35.  *    retrieve (EMP.salary) where EMP.name = "Fred"
  36.  * which will cuase yet another activation of the rule etc....
  37.  *
  38.  * The Solution:
  39.  * Every time we try to calculate the value of a tuple's attribute
  40.  * by activating a rule, we put in a stack the correpsonding rule oid,
  41.  * tuple oid and attribute number.
  42.  * Before doing that though, we first check to see if there is already
  43.  * a similar entry (same ruleId, tuple oid * attribute number) in the
  44.  * stack. If yes, then we have entered a loop.
  45.  * If no, we go ahead and execute the rule. When the rule has finished
  46.  * its execution, we remove the entry from the stack.
  47.  * Note that the length of the stack is proprtional to the depth
  48.  * of the recursion in the ExecMain/rule manager calls.
  49.  *
  50.  * Some Options:
  51.  * Once we have discovered a rule loop we have to decide what to do.
  52.  * Some alternatives:
  53.  *  1) Abort transaction! (not a good solution, as in some cases
  54.  *     (as in the example above with Fred's & Mike's salary) the rule
  55.  *     is OK, and the loop is created because of the implementation.
  56.  *  2) Ignore this rule and either activate other rules that possibly
  57.  *     exist, or use the value as stored in the tuple.
  58.  *  3) Ignore the tuple altogether!
  59.  *
  60.  * MEMORY MANAGEMENT PROBLEMS:
  61.  * Every time we push an item in the stack we first check if we have
  62.  * enough space. If not, we allocate some new space, copty there the
  63.  * contents of the old one and then free the old one.
  64.  * However, as each recursive call of 'prs2Main' changes the memory
  65.  * context (we do that to avoid memory leaks - see comments in prs2main.c)
  66.  * it is possible that we allocate some space under a certain mem cntxt
  67.  * and when we want to reallocate some more we are under another.
  68.  * This of course causes bad things to happen and our beloved postgres
  69.  * dies a horrible death.
  70.  * So I did an extensive study of all the possible alternatives
  71.  * carefully examining all the tradeoff involved, taking into account
  72.  * all possible softawre engineering considerations, and finally after an
  73.  * extensive period of thought (approximately 10 seconds) I decided
  74.  * to create (yes!) yet another memory context specially designed and
  75.  * custom made for the rule stack.
  76.  *
  77.  *===================================================================
  78.  */
  79.  
  80. #include "tmp/c.h"
  81. #include "utils/log.h"
  82. #include "rules/prs2.h"
  83. #include "nodes/mnodes.h"
  84. #include "utils/mcxt.h"
  85.  
  86.  
  87. #define PRS2_INCREMENT_STACKSIZE 1
  88.  
  89. /*-----
  90.  * this is a local static variable :
  91.  * Rule Stack Memeory Context!
  92.  */
  93. static GlobalMemory Prs2RuleStackMemoryContext = NULL;
  94.  
  95. /*-------------------------------------------------------------------
  96.  * prs2RuleStackPush
  97.  *
  98.  *  Add a new entry to the rule stack. First check if there is enough
  99.  * stack space. otherwise reallocate some more memory...
  100.  */
  101. void
  102. prs2RuleStackPush(p, ruleId, tupleOid, attributeNumber)
  103. Prs2EStateInfo p;
  104. ObjectId ruleId;
  105. ObjectId tupleOid;
  106. AttributeNumber attributeNumber;
  107. {
  108.     Prs2Stack temp;
  109.     int newSize;
  110.     MemoryContext oldMemContext;
  111.  
  112.     /*
  113.      * remember the stack pointer always points
  114.      * to the next free stack entry...
  115.      */
  116.     if (p->prs2StackPointer >= p->prs2MaxStackSize) {
  117.     /*
  118.      * we need to allocate some more stack room!
  119.      * switch to `Prs2RuleStackMemoryContext' for all memory
  120.      * allocations
  121.      */
  122.     if (Prs2RuleStackMemoryContext == NULL) {
  123.         elog(WARN,"prs2RuleStackFree: Prs2RuleStackMemoryContext is NULL!");
  124.     }
  125.     oldMemContext =
  126.         MemoryContextSwitchTo((MemoryContext)Prs2RuleStackMemoryContext);
  127.     /*
  128.      * allocate more room & free the old one
  129.      */
  130.     newSize = p->prs2MaxStackSize + PRS2_INCREMENT_STACKSIZE;
  131.     temp = (Prs2Stack) palloc(sizeof(Prs2StackData) * newSize);
  132.     if (temp==NULL) {
  133.         elog(WARN,
  134.         "prs2RuleStackPush: out of memory (from %d to %d entries)",
  135.         p->prs2MaxStackSize, newSize);
  136.     }
  137.     if (p->prs2Stack != NULL) {
  138.         bcopy(p->prs2Stack,
  139.             temp,
  140.             sizeof(Prs2StackData)*p->prs2MaxStackSize);
  141.         pfree(p->prs2Stack);
  142.     }
  143.     p->prs2Stack = temp;
  144.     p->prs2MaxStackSize = newSize;
  145.     /*
  146.      * switch back to the old mem context
  147.      */
  148.     (void) MemoryContextSwitchTo(oldMemContext);
  149.     }
  150.  
  151.     /*
  152.      * there is enough space, go and add the new entry..,
  153.      */
  154.  
  155.     p->prs2Stack[p->prs2StackPointer].ruleId = ruleId;
  156.     p->prs2Stack[p->prs2StackPointer].tupleOid = tupleOid;
  157.     p->prs2Stack[p->prs2StackPointer].attrNo = attributeNumber;
  158.  
  159.     p->prs2StackPointer += 1;
  160.  
  161. }
  162.  
  163. /*-------------------------------------------------------------------
  164.  * prs2RuleStackPop
  165.  *
  166.  *  Discard the top entry of the stack
  167.  */
  168. void
  169. prs2RuleStackPop(p)
  170. Prs2EStateInfo p;
  171. {
  172.     /*
  173.      * the stackPointer always points to the first free
  174.      * stack item
  175.      */
  176.     if (p->prs2StackPointer <= 0) {
  177.     elog(WARN, "prsRuleStackPop: Empty rule stack!");
  178.     }
  179.  
  180.     p->prs2StackPointer -= 1;
  181.  
  182. }
  183.  
  184. /*-------------------------------------------------------------------
  185.  * prs2RuleStackSearch
  186.  *
  187.  *   Search for a stack entry matching the given arguments.
  188.  *   Return true if found, false otherwise...
  189.  */
  190. bool
  191. prs2RuleStackSearch(p, ruleId, tupleOid, attributeNumber)
  192. Prs2EStateInfo p;
  193. ObjectId ruleId;
  194. ObjectId tupleOid;
  195. AttributeNumber attributeNumber;
  196. {
  197.     int i;
  198.  
  199.     /*
  200.      * the stackPointer always points to the first free stack
  201.      * element
  202.      */
  203.     for (i=0; i<p->prs2StackPointer; i++) {
  204.     if (p->prs2Stack[i].ruleId == ruleId &&
  205.         p->prs2Stack[i].tupleOid == tupleOid &&
  206.         p->prs2Stack[i].attrNo == attributeNumber) {
  207.         return(true);
  208.     }
  209.     }
  210.  
  211.     return(false);
  212. }
  213.  
  214. /*-------------------------------------------------------------------
  215.  * prs2RuleStackInitialize
  216.  *
  217.  *   Intialize the stack.
  218.  */
  219. Prs2EStateInfo
  220. prs2RuleStackInitialize()
  221. {
  222.     Prs2EStateInfo p;
  223.     MemoryContext oldMemContext;
  224.  
  225.     /*
  226.      * switch to `Prs2RuleStackMemoryContext' for all memory
  227.      * allocations
  228.      */
  229.     if (Prs2RuleStackMemoryContext == NULL) {
  230.     Prs2RuleStackMemoryContext = CreateGlobalMemory("*prs2RuleStack*");
  231.     }
  232.     oldMemContext =
  233.     MemoryContextSwitchTo((MemoryContext)Prs2RuleStackMemoryContext);
  234.  
  235.     /*
  236.      * create the stack
  237.      */
  238.     p = (Prs2EStateInfo) palloc(sizeof(Prs2EStateInfoData));
  239.     if (p==NULL) {
  240.     elog(WARN,"prs2RuleStackInitialize: palloc(%ld) failed.\n",
  241.     sizeof(Prs2EStateInfoData));
  242.     }
  243.     p->prs2StackPointer = 0;
  244.     p->prs2MaxStackSize = 0;
  245.     p->prs2Stack = NULL;
  246.  
  247.     /*
  248.      * switch back to the original mem context
  249.      */
  250.     (void) MemoryContextSwitchTo(oldMemContext);
  251.  
  252.     return(p);
  253. }
  254.  
  255. /*-------------------------------------------------------------------
  256.  * prs2RuleStackFree
  257.  *
  258.  *   Free the memory occupied by the stack.
  259.  */
  260. void
  261. prs2RuleStackFree(p)
  262. Prs2EStateInfo p;
  263. {
  264.     MemoryContext oldMemContext;
  265.  
  266.     /*
  267.      * switch to `Prs2RuleStackMemoryContext' for all memory
  268.      * deallocations
  269.      */
  270.     if (Prs2RuleStackMemoryContext == NULL) {
  271.     elog(WARN,"prs2RuleStackFree: Prs2RuleStackMemoryContext is NULL!");
  272.     }
  273.     oldMemContext =
  274.     MemoryContextSwitchTo((MemoryContext)Prs2RuleStackMemoryContext);
  275.  
  276.     /*
  277.      * free the Prs2EStateInfo
  278.      */
  279.     if (p!= NULL && p->prs2Stack != NULL) {
  280.     pfree(p->prs2Stack);
  281.     }
  282.     if (p!=NULL)
  283.     pfree(p);
  284.  
  285.     /*
  286.      * switch back to the original mem context
  287.      */
  288.     (void) MemoryContextSwitchTo(oldMemContext);
  289.  
  290. }
  291.